﻿using System;
using System.Collections.Generic;
using Swift;
using Swift.Math;

namespace SCM
{
    /// <summary>
    /// 地图单位，包括兵、建筑、中立单位等
    /// </summary>
    public class Unit : SerializableData
    {
        // 整个战场生命周期内唯一 ID
        string uid;
        public string UID { get { return uid; } }
        Unit() { uid = null; } // 序列化用
        public Unit(string uniqueID) { uid = uniqueID; }

        // 单位类型名称
        string type;
        public string UnitType { get { return type; } set{ type = value; _cfg = UnitConfiguration.GetConfig(type, lv); } }

        // 所处位置
        Vec2 pos;
        public Vec2 Pos { get { return pos; } set { pos = value; } }

        // 方向 [0, 360) degree
        Fix64 dir;
        public Fix64 Dir { get { return dir; } set { dir = value; } }

        // 血量
        Fix64 hp;
        public Fix64 Hp { get { return hp; } set { hp = MathEx.Clamp(value, Fix64.Zero, cfg.MaxHp); } }
        
        public Fix64 PowerAdd { get; set; } // 攻击力增加值
        public Fix64 DefenceAdd { get; set; } // 防御力增加值

        // 实时攻击防御值
        public Fix64 Power { get { return cfg.Power + PowerAdd; } }
        public Fix64 Defence { get { return cfg.Defence + DefenceAdd; } }

        // 是否建造完成
        bool buildingCompleted;
        public bool BuildingCompleted { get { return buildingCompleted; } set { buildingCompleted = value; } }

        // 所属房间
        public Room Room { get; set; }

        // 附件数
        public List<Unit> Accessories { get { return accessories; } }
        List<Unit> accessories = new List<Unit>();

        // 战斗单位建造列表
        public List<string> UnitCosntructingWaitingList { get { return unitCosntructingWaitingList; } }
        List<string> unitCosntructingWaitingList = new List<string>();

        // 已经建造好的单位
        public List<string> UnitConstructed { get { return unitConstructed; } }
        List<string> unitConstructed = new List<string>();

        // 所属主单位
        public Unit Owner { get; set; }

        // 移动时忽略可攻击目标
        public bool IgnoreTargetWhenMoving;

        // 所属玩家
        int player;
        public int Player { get { return player; } set { player = value; } }

        // 是否是中立单位
        public bool IsNeutral { get { return player == 0; } }

        // 等级
        public int Level
        {
            get
            {
                return lv;
            }
            set
            {
                lv = value;
                _cfg = UnitConfiguration.GetConfig(type, lv);
            }
        } int lv;

        // 目标移动位置
        public List<Vec2> MovePath = new List<Vec2>();
        public void ResetPath(IEnumerable<Vec2> path)
        {
            MovePath.Clear();
            if (path != null)
                MovePath.AddRange(path);
        }

        // RVOAgent ID
        public int RVOAgentID { get; set; }

        // 一般性附加数据
        public object Tag { get; set; }

        // 设置行进速度
        public Vec2 PreferredVelocity
        {
            get { return pv; }
            set { pv = value; }
        } Vec2 pv;

        // 配置信息
        public UnitConfigInfo cfg
        {
            get
            {
                return _cfg;
            }
        } UnitConfigInfo _cfg;

        // 建造完成
        public void BuildingComplete()
        {
            BuildingCompleted = true;
            Room.OnBuildingConstructingCompleted(this);
        }

        // 承受伤害
        public void OnDamage(int damage)
        {
            // 伤害先转移到附件
            if (Accessories.Count > 0)
            {
                var a = Accessories[0];
                a.OnDamage(damage);
            }
            else
            {
                hp -= damage;
                hp = hp.Clamp(Fix64.MinValue, cfg.MaxHp);
            }
        }

        // 是否满足空地攻击条件
        public bool CanAttack(Unit target)
        {
            if (target == null || target.Hp <= 0)
                return false;
            else if (cfg.Power > Fix64.Zero && player == target.player) // 敌方伤害
                return false;
            else if (cfg.Power < Fix64.Zero && player != target.player) // 己方治疗
                return false;
            else if (cfg.Power == Fix64.Zero || target.cfg.Unattackable)
                return false;
            else if (cfg.CanAttackAir && target.cfg.IsAirForce)
                return true;
            else if (cfg.CanAttackGround && !target.cfg.IsAirForce)
                return true;
            else
                return false;
        }

        // 是否在攻击范围内
        public bool InAttackRange(Unit target)
        {
            if (target == null)
                return false;

            var compD = cfg.AttackRange + target.cfg.RealSize;
            return (target.pos - pos).Length2 <= compD * compD;
        }

        // 获取在攻击范围内的目标
        public Unit[] GetUnitsInAttackRange(Func<Unit, bool> filter)
        {
            return FC.Select(Room.AllUnits,
                (tar) => (filter == null || filter(tar)) && InAttackRange(tar)).ToArray();
        }

        // 选择范围内的可攻击目标，并按优先级排序
        Dictionary<Unit, Fix64> lenDict = new Dictionary<Unit, Fix64>();
        public Unit[] GetAllAttackableUnitsInRangeByOrder(Fix64 range)
        {
            // 这个是非常高频的部分，值得仔细优化
            lenDict.Clear();
            var ts = FC.Select(Room.AllUnits,
                (t) => {
                    if (t.Hp <= Fix64.Zero || !CanAttack(t))
                        return false;
                    
                    var d = t.pos - pos;
                    var compareD = range + t.cfg.RealSize;
                    if (d.y.Abs() > compareD)
                        return false;
                    else if (d.x.Abs() > compareD)
                        return false;
                    else
                    {
                        var l2 = d.Length2;
                        lenDict[t] = l2;
                        return l2 <= compareD * compareD;
                    }
                }
            ).ToArray();

            ts.SwiftSort((t) =>
            {
                return lenDict[t];
            });
            return ts;
        }

        // 选择范围内的可攻击目标，选择最高优先级的攻击目标
        public Unit GetFirstAttackableUnitsInRange(Fix64 range)
        {
            var ts = GetAllAttackableUnitsInRangeByOrder(range);
            return ts.Length == 0 ? null : ts[0];
        }

        override protected void Sync()
        {
            BeginSync();
            SyncString(ref uid);
            SyncString(ref type);
            SyncFix64(ref pos.x);
            SyncFix64(ref pos.y);
            SyncFix64(ref dir);
            SyncFix64(ref hp);
            SyncInt(ref buildingCompleted);
            SyncListString(ref unitCosntructingWaitingList);
            SyncInt(ref player);
            EndSync();
        }
    }
}
